//
//  foc_nlp.cpp
//  NLCEQ_mixed
//
//  Created by Duong Ngo on 8/13/16.
//  Copyright © 2016 Duong Ngo. All rights reserved.
//

#include "foc_nlp.hpp"
#include "global.hpp"
#include "mapping.hpp"
#include "Jacobian.hpp"

#include <cassert>

/** Constructor */
foc_nlp::foc_nlp(const std::vector<double>& x_init)
{

    a_init_ = new Number[var_foc];
    for (int i=0; i<var_foc; ++i){
        a_init_[i]= x_init[i];
    }
}

/** Destructor */
foc_nlp::~foc_nlp()
{}

/** Return the size of problem */
bool foc_nlp::get_nlp_info(Index& n, Index& m, Index& nnz_jac_g,
                              Index& nnz_h_lag, IndexStyleEnum& index_style)
{
    n= var_foc;
    m= gt_foc;
    nnz_jac_g= 68;
    nnz_h_lag= 14;
    index_style= TNLP::C_STYLE;
    return true;
}

/** Return the variables bound */
bool foc_nlp::get_bounds_info(Index n, Number* x_l, Number* x_u,
                                 Index m, Number* g_l, Number* g_u)
{

    for (int i=0; i<n; ++i){
        x_l[i]=-2e19;
        x_u[i]= 2e19;
    }
    
    for (int i=0; i<m; ++i){
        g_l[i]=0;
        g_u[i]=0;
    }

    
    return true;
}

/** Return the starting point */
bool foc_nlp::get_starting_point(Index n, bool init_x, Number* x,
                                    bool init_z, Number* z_L, Number* z_U,
                                    Index m, bool init_lambda,
                                    Number* lambda)
{
    assert(init_x == true);
    for (int i=0; i<n; ++i){
        x[i]= a_init_[i];
    }
    return true;
}

/** Evaluate the objective function */
bool foc_nlp::eval_f(Index n, const Number* x, bool new_x, Number& obj_value)
{
    obj_value = 0;
    return true;
}

/** Evaluate the gradient of the objective function */
bool foc_nlp::eval_grad_f(Index n, const Number* x, bool new_x, Number* grad_f)
{
    for (int i=0; i<n; ++i){
        grad_f[i]=0;
    }
    
    return true;
}

/** Evaluate the residual of the constraints */
bool foc_nlp::eval_g(Index n, const Number* x, bool new_x, Index m, Number* g)
{
    variables s;
    find_variable_s(x, s);
    
    //Bankers
    g[0]= s.gamma*s.cb -1;
    g[1]= s.Rf- 1./beta_b;
    g[2]= s.gamma- beta_b*s.Rm*s.gamma- varphi*s.mur;
    g[3]= s.gamma- beta_b*Rn*s.gamma- s.mur;
    g[4]= s.ql- (beta_b*deltab-thetaa)/(1-beta_b*deltab);
    g[5]= s.tau;
    g[6]= s.m- (s.Rm*s.m+ s.ql*s.s+ (thetaa-deltab)*s.bh+ s.cb- (Rn-1)*s.n);
    g[7]= s.n- varphi*s.m;
    g[8]= s.n+ (1-kappa)*s.bh- s.m -s.muc;
    g[9]= (1-deltab)*s.bh - s.s ;
    
    
    //Households
    g[10]= 1/s.ch - s.etaz- s.lama;
    g[11]= s.ch*s.lamb -1;
    g[12]= s.lama -beta_h*s.Rm*s.lamb;
    g[13]= s.ql*s.lamb- (beta_h*(deltab + deltab*s.ql)*s.lamb +pe*pow(s.etab,2));
    g[14]= s.lamb -(beta_h*(1-deltak)*s.lamb +beta_h*alphaa*s.pm*s.lama*s.y/s.k);
    g[15]= s.Rm*s.m+ s.ql*s.s- s.ch- s.i- deltab*s.bh;
    g[16]= bh_limit- s.bh+ s.etab ;
    
    //Inflation & Output
    g[17]= s.pm -(epsilon-1)/epsilon;
    g[18]= s.y- pow(s.k,alphaa)*pow(s.l, 1-alphaa);
   
    //Central Bnk
    g[19]= s.u - 1/beta_b;
    
    //Market Clearing
    g[20]= s.y- (s.cb+ s.ch+ s.i+ thetaa*s.bh);
    g[21]= deltak*s.k- s.i;
    g[22]= s.ip - 1;
    g[23]= s.pie - 1;
    g[24]= chi*pow(s.l, nu+1)- (1-alphaa)*s.pm*s.y*s.lama;
    
    return true;
}

/* Return the Jacobian of g */
bool foc_nlp::eval_jac_g(Index n, const Number* x, bool new_x,
                            Index m, Index nele_jac, Index* iRow, Index *jCol,
                            Number* values)
{
    int dem=0; //track the position of nonzero element in the Jacobian
    if (values == NULL){
        // retun the structure of the Jacobian
        dem=0;
        std::map<std::string, int> f;
        find_map(f);
        find_jacobian_structure(f, iRow, jCol, dem);
        printf("Jac Struct dem=%2u\n",dem);
        assert(dem=nele_jac);
    }
    else {
        //return the values of Jacobian
        dem=0;
        variables s;
        find_variable_s(x, s);
        find_jacobian_values(s, values, dem);        
        printf("Jac Values dem=%2u\n",dem);
        assert(dem=nele_jac);
    }
    return true;
}

/** Return the Hessian Matrix */
bool foc_nlp::eval_h(Index n, const Number* x, bool new_x,
                        Number obj_factor, Index m, const Number* lambda,
                        bool new_lambda, Index nele_hess, Index* iRow,
                        Index* jCol, Number* values)
{
       return false;
}

/** Finalize the Solution */
void foc_nlp::finalize_solution(SolverReturn status,
                                   Index n, const Number* x, const Number* z_L, const Number* z_U,
                                   Index m, const Number* g, const Number* lambda,
                                   Number obj_value,
                                   const IpoptData* ip_data,
                                   IpoptCalculatedQuantities* ip_cq)
{
    result.resize(n);
    for (int i=0; i<n; ++i){
        result[i]= x[i];
    }
}













